# -*- coding: utf-8 -*-
import copy
import math
from abc import ABC
from uuid import uuid1
from datetime import datetime

from app.configs import kj236_pool, global_config
from app.models import db_session
from app.models.model import TrackModel, PositionModel
from app.tools.utils import THRESHOLD
from app.tools.utils import dt2str
from app.tools.utils import str2dt
from app.tools.utils import get_item_xy
from app.tools.utils import clear_same_points
from app.tools.utils import point_at_polyline
from app.tools.utils import distance
from app.views import CommonHandler
from apscheduler.schedulers.tornado import TornadoScheduler

older_cars, cars = {}, []


def get_tracks():
    session = db_session()
    features = session.query(TrackModel).filter(
        TrackModel.meta_id == global_config['mine_code'],
        TrackModel.dxftype == "vehicle"
    ).all()
    session.close()

    keys = [feature.id for feature in features]
    values = [feature.vector() for feature in features]
    clear_same_points(values)
    return dict(zip(keys, values))


def get_positions():
    session = db_session()
    features = session.query(PositionModel).filter(
        PositionModel.meta_id == global_config['mine_code'],
        PositionModel.dxftype == "vehicle"
    ).all()
    session.close()

    keys = [feature.node_id for feature in features]
    values = [feature.vector() for feature in features]
    return dict(zip(keys, values))


def angle(grid, direction, left=False):
    if direction == 0:
        if left:
            result = grid - math.pi / 2
        else:
            result = grid + math.pi / 2
    else:
        if left:
            result = grid + math.pi / 2
        else:
            result = grid - math.pi / 2
    return result


def get_cars():
    global older_cars

    # 从密采表里获取实时更新时间
    key_cars, items = {}, kj236_pool.select("SELECT * from m_car")
    for item in items:
        key_cars[item[1]] = item[26]

    cars, items = [], kj236_pool.select("SELECT * from view_vehicle_in_mine")
    for item in items:
        # 这里主要做车辆状态的检测,只要和上次更新时间不同,就认为车辆状态真实
        ack = 0
        if item[2] in older_cars:
            ot_str = older_cars[item[2]][-1]['work_update_time']
            if ot_str is not None:
                sec = (item[20] - str2dt(ot_str)).seconds
                ack = 0 if 0 < sec < 30 else older_cars[item[2]][-1]['ack'] + 1

        speed = 0 if item[12] is None else item[12]
        mileage = 0 if item[13] is None else item[13]
        water_temp = 0 if item[14] is None else item[14]
        ch4_conc = 0 if item[15] is None else item[15]
        surface_temp = 0 if item[16] is None else item[16]
        exhaust_temp = 0 if item[17] is None else item[17]
        rpm = 0 if item[18] is None else item[18]
        battery = 0 if item[19] is None else item[19]

        # 从视图表中读取车辆信息数据
        car = {
            "id": item[2],
            "carcode": item[2],
            "carid": item[4],
            "name": item[5],
            "driver": "" if item[22] is None else item[22],
            "type": int(item[6]),
            "inminetime": dt2str(item[10]),
            "intime": dt2str(item[11]),
            "readerid": item[0],
            "dir": item[8],
            "distance": round(item[9] / 100, 2),
            "yukirin": round(item[9] / 100, 2),
            "regname": item[1],
            "speed": speed if ack < 6 else 0,
            "mileage": 0 if mileage is None else mileage,
            "water_temp": water_temp if ack < 6 else 0,
            "ch4_conc": ch4_conc if ack < 6 else 0,
            "surface_temp": surface_temp if ack < 6 else 0,
            "exhaust_temp": exhaust_temp if ack < 6 else 0,
            "rpm": rpm if ack < 6 else 0,
            "battery": battery if ack < 6 else 0,
            "time": dt2str(key_cars[item[2]]) if item[2] in key_cars else None,
            "work_update_time": dt2str(item[20]),
            "ack": ack,
        }
        cars.append(car)

    return cars


def stat(reader_id, old_cars):
    result = {
        'larger': 0, 'smaller': 0, 'avg': 0, 'distance': 0,
        'left': 0, 'right': 0, 'up': 0, 'down': 0, 'still': 0
    }

    # 计算向左、右或者向上、下的数量
    for i in range(len(old_cars) - 1):
        c, n = old_cars[i], old_cars[i + 1]
        d = distance(c['x'], c['y'], n['x'], n['y'])
        if d > 1:
            result['right'] = result['right'] + 1 if c['x'] < n['x'] else result['right']
            result['left'] = result['left'] + 1 if c['x'] > n['x'] else result['left']
            result['up'] = result['up'] + 1 if c['y'] < n['y'] else result['up']
            result['down'] = result['down'] + 1 if c['y'] > n['y'] else result['down']

    # 取出统一分站的信息
    cars = [i for i in old_cars if i['readerid'] == reader_id]
    num = len(cars)
    if num < 2:
        return result

    # 每两个点进行比较,查看这十个点的整体状态
    for i in range(num - 1):
        # 取出前后两个点,获取相对于分站的距离
        c, n = cars[i], cars[i + 1]
        d1, d2 = abs(c['distance']), abs(n['distance'])

        # 计算逐渐变小或者逐渐变大的数量
        result["distance"] += abs(d1 - d2)
        if abs(d1 - d2) > THRESHOLD:
            result['smaller'] = result['smaller'] + 1 if d1 > d2 else result['smaller']
            result['larger'] = result['larger'] + 1 if d2 > d1 else result['larger']

        # 判断同分站最近连续的几个点是否位置相同
        if distance(c['x'], c['y'], n['x'], n['y']) < THRESHOLD:
            result['still'] = result['still'] + 1 if i > 3 else result['still']

    result['avg'] = result['distance'] / num
    return result


def do_car_alarm(car, key_cars, key_deps, key_readers, car_tracks):
    # 判断当前车辆的坐标是否在限制范围内,统计一下数量
    in_range_num, out_range_num = 0, 0
    for car_track in car_tracks:
        now, car_code = datetime.now(), car_track['car_code']
        start_time, end_time = car_track['start_time'], car_track['end_time']
        # 找到满足条件的范围记录,然后判断车辆坐标是否在范围内
        if car_code == car['carcode'] and start_time <= now <= end_time:
            result = point_at_polyline([car['x'], car['y']], car_track['coordinates'])
            in_range_num = in_range_num + 1 if result else in_range_num + 0
            out_range_num = out_range_num + 1 if not result else out_range_num + 0

    # 当前出现了车辆不在范围的情况
    if in_range_num == 0 and out_range_num > 0:
        query = """
            SELECT * FROM m_car_alarm 
            WHERE carid='%s' AND alarmtype='路线偏离' AND endtime is null
        """ % (car['carid'])
        items = kj236_pool.select(query)

        # 如果原来已经存在告警,则不再插入新的告警
        if len(items) <= 0 and car['carid'] in key_cars:
            car = key_cars[car['carid']]
            dep_name = key_deps[car[5]] if car[5] in key_deps else ""
            rdnum = car[8] if car[8] else ""
            rdname = key_readers[rdnum]["placement"] if rdnum in key_readers else ""
            regname = key_readers[rdnum]["regname"] if rdnum in key_readers else ""
            alarm = """
                INSERT INTO m_car_alarm(
                    alarmid, alarmtype, alarmname, carid, carcode, carname, 
                    begintime, createtime, deptname, rdnum, rdname, regname, 
                    speed, inminetime, batteryvoltage
                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
            """
            values = (
                str(uuid1()), "路线偏离", "路线偏离", car[0], car[1], car[2], datetime.now(),
                datetime.now(), dep_name, rdnum, rdname, regname, car[25], car[6], car[15]
            )
            kj236_pool.execute(alarm, values)

    # 当前出现了车辆在范围内,更新一下结束时间
    if in_range_num > 0:
        update_query = """
            UPDATE m_car_alarm SET endtime = %s 
            WHERE carid=%s AND alarmtype='路线偏离' AND endtime is null
        """
        values = (datetime.now(), car['carid'])
        kj236_pool.execute(update_query, values)


def get_car_xys(cars, positions, tracks):
    global older_cars

    # 获取车辆可以形式的规定范围轨迹线记录
    sql = "SELECT car_id, start_time, end_time, coordinates from m_car_track"
    car_tracks, items = [], kj236_pool.select(sql)
    for item in items:
        car_track = {
            "car_code": item[0],
            "start_time": item[1],
            "end_time": item[2],
            "coordinates": item[3],
        }
        car_tracks.append(car_track)

    # 获取车辆信息
    key_cars, items = {}, kj236_pool.select("SELECT * from m_car")
    for item in items:
        key_cars[item[0]] = item

    # 获取部门信息
    key_deps, items = {}, kj236_pool.select("SELECT * from m_dep")
    for item in items:
        key_deps[item[0]] = item[1]

    # 获取部门信息
    key_readers, items = {}, kj236_pool.select("SELECT * from m_reader")
    for item in items:
        key_readers[item[0]] = {"placement": item[5], "regname": item[3]}

    for car in cars:
        # 判断分站是否能存在
        if car['readerid'] is None:
            continue

        # 获取分站的位置
        node_id = int(car['readerid'])
        if node_id not in positions:
            continue
        ltcar, position = None, positions[node_id]

        # 得到最近一次car记录信息
        if car['id'] in older_cars:
            ltcar = copy.deepcopy(older_cars[car['id']][-1])

        # 第一次获取数据,还没有上次的car记录
        if ltcar is None:
            # 计算车辆在地图上的坐标和方向
            xy = get_item_xy(tracks, position, car['dir'], car['distance'])
            car['x'], car['y'], car['grid'] = xy['x'], xy['y'], xy['grid']

            # 处理车辆辅助预警
            do_car_alarm(car, key_cars, key_deps, key_readers, car_tracks)

            # 左右方向和角度设置为默认,也就是说车头方向未知
            car['left'], car['angle'] = False, angle(car['grid'], car['dir'])
            older_cars[car['id']] = [car]
            continue

        # 统计已经出现的10个点的状态
        stas = stat(car['readerid'], older_cars[car['id']])

        # 计算一下和上次记录的时间间隔
        second = 0
        if ltcar['time'] is not None:
            t1, t2 = datetime.now(), str2dt(ltcar['time'])
            second = (t1 - t2).seconds

        # 这里进行速度补偿,时间控制在3分钟,still代表最后四次的位置相同
        if 2 < second < 300 and not stas['still'] > 5:
            speed = round(car['speed'] / 3.6, 2)
            # 这里要先计算一下补偿后的结果
            dv = round(ltcar['distance'] + speed, 2)
            xy = get_item_xy(tracks, position, car['dir'], dv)

            # 看是否满足和当前车辆行使同向
            if ((stas['left'] > stas['right'] and xy['x'] < ltcar['x'])
                    or (stas['left'] < stas['right'] and xy['x'] > ltcar['x'])):
                car['distance'] = dv

        # 如果和上次的距离在1.5米范围内,则不做任何移动
        if ltcar['readerid'] == car['readerid']:
            d = math.fabs(ltcar["distance"] - car['distance'])
            if d < 1.5:
                # 保存上一次记录的状态
                car['x'], car['y'], car['angle'] = ltcar['x'], ltcar['y'], ltcar['angle']
                car['left'], car['grid'] = ltcar['left'], ltcar['grid']

                # 处理车辆辅助预警
                do_car_alarm(car, key_cars, key_deps, key_readers, car_tracks)

                # 将记录添加到历史记录列表中,并保持只包含10个元素
                older_cars[car['id']].append(copy.deepcopy(car))
                if len(older_cars[car['id']]) > 11:
                    del older_cars[car['id']][0]
                continue

        # 计算车辆在地图上的坐标和方向
        xy = get_item_xy(tracks, position, car['dir'], car['distance'])
        car['x'], car['y'], car['grid'] = xy['x'], xy['y'], xy['grid']

        # 第一条记录,计算出左右方向和角度
        car['angle'] = angle(car['grid'], car['dir'], ltcar['left'])
        car['left'] = car['x'] < ltcar['x']

        # 将记录添加到历史记录列表中,并保持只包含10个元素
        older_cars[car['id']].append(copy.deepcopy(car))
        if len(older_cars[car['id']]) > 11:
            del older_cars[car['id']][0]

        # 处理车辆辅助预警
        do_car_alarm(car, key_cars, key_deps, key_readers, car_tracks)

    return cars


def job():
    global cars
    tracks, positions, cars = get_tracks(), get_positions(), get_cars()
    cars = get_car_xys(cars, positions, tracks)


def run_cache():
    scheduler = TornadoScheduler()
    scheduler.add_job(job, 'interval', seconds=1, id="vehicle_cache")
    scheduler.start()


# 只有KJ236项目执行此缓存操作
# if 'vehicle' in global_config and global_config['vehicle']['backend']:
#     run_cache()


class VehicleHandler(CommonHandler, ABC):
    async def get(self):
        global cars
        await self.response(data={"cars": cars})
